/*
 *  ifxconnection.hpp - Informix dbExpress connection class
 *
 *    Copyright (c) 2001-2006, Luxena Software Company. All rights reserved.
 *
 *  Purpose:
 *
 */

#ifndef __IFXCONNECTION_HPP__
#define __IFXCONNECTION_HPP__

#ifndef __cplusplus
#error This header requires a C++ compiler ...
#endif

#include <list>
#include <ifxtypes.h>
#include <sqlca.h>
#include "cstr.hpp"
#include "hashtable.hpp"
#include "dbxpress.hpp"
#include "dbxcommon.hpp"
#include "ifxdefs.hpp"
#include "ifxdriver.hpp"


/////////////////////////////////////////////////////////////////////////////
// CInformixTypesMapping

struct CInformixTypesMapping
{
	CInformixTypesMapping ():
		decimal   (fldBCD,     0         ),
		money     (fldFLOAT,   fldstMONEY),
		int8      (fldFMTBCD,  0         ),
		lvarchar  (fldZSTRING, 0         ) {}

	DataType decimal;
	DataType money;
	DataType int8;
	DataType lvarchar;
};


/////////////////////////////////////////////////////////////////////////////
// IInformixConnection - extension of SQLConnection interface for Informix

typedef void (API_CALL_TYPE *TimeOutCallback) (void *customData, int status);

struct IInformixConnection : public SQLConnection
{
	COM_METHOD(sqlca_s *) SQLCA () = 0;
	COM_METHOD(const char *) errorMessage () = 0;
	SQL_METHOD setTimeOutCallback (long timeOut, TimeOutCallback callback, void *customData) = 0;
	COM_METHOD(bool) interruptSQL () = 0;
	COM_METHOD(bool) getLastInsertedSerial8 (void *value) = 0;
	COM_METHOD(const char *) serverVersion () = 0;
	COM_METHOD(bool) isDynamicServer () = 0;
	COM_METHOD(int) isUniversalServer () = 0;
	COM_METHOD(int) sessionID () = 0;
} com_interface;


/////////////////////////////////////////////////////////////////////////////
// CInformixConnection - IInformixConnection interface implementation

struct IDatabaseChangeObserver
{
	virtual void databaseChanged () = 0;
};

class CInformixConnection : public CErrorHandlingImpl< CUnknownImpl<IInformixConnection> >,
	public IErrorRegister
{
public:
	enum ThreadMode
	{
		ONE_PER_THREAD,
		MANY_PER_THREAD,
		INTERTHREADED_FETCH_OPTIMIZED,
		INTERTHREADED
	};

	CInformixConnection (CInformixDriver *driver);
	virtual ~CInformixConnection ();

	CInformixActivityPtr activate (bool allowAutoCommit, bool saveConnectionArea, bool forFetch = false);

	void setAutoCommit (bool value);
	int4 getBlobSize ();
	void setBlobSize (int4 value);
	CCallback * getCallback ();
	void setClientLocale (const char *value);
	int4 getCommitRetain ();
	void setCommitRetain (int4 value);
	void setDatabaseLocale (const char *value);
	void setIsolationLevel (eXILType value, bool retainUpdateLocks = false);
	bool getIsOnLine ();
	void setIsOnLine (bool value);
	const char * getServerVersion ();
	void setThreadMode(ThreadMode value);
	bool getTrimChar ();
	void setTrimChar(bool value);
	CInformixTypesMapping * getTypesMapping ();
	bool isUniversal ();
	void setWaitOnLocks(int value);

	void attachDatabaseChangeObserver (IDatabaseChangeObserver *observer);
	void detachDatabaseChangeObserver (IDatabaseChangeObserver *observer);
	void registerDatabaseChange ();

// SQLConnection
	SQL_METHOD connect (pCHAR pszServerName, pCHAR pszUserName, pCHAR pszPassword);
	SQL_METHOD disconnect ();
	SQL_METHOD getSQLCommand (ppSQLCommand ppComm);
	SQL_METHOD getSQLMetaData (ppSQLMetaData ppMeta);
	SQL_METHOD setOption (eSQLConnectOption eCOption, INT32 lValue);
	SQL_METHOD getOption (eSQLConnectOption eCOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength);
	SQL_METHOD beginTransaction (UINT32 ulTransDesc);
	SQL_METHOD commit (UINT32 ulTransDesc);
	SQL_METHOD rollback (UINT32 ulTransDesc);
// IInformixConnection
	COM_METHOD(sqlca_s *) SQLCA ();
	COM_METHOD(const char *) errorMessage ();
	SQL_METHOD setTimeOutCallback (long timeOut, TimeOutCallback callback, void *customData);
	COM_METHOD(bool) interruptSQL ();
	COM_METHOD(bool) getLastInsertedSerial8 (void *value);
	COM_METHOD(const char *) serverVersion ();
	COM_METHOD(bool) isDynamicServer ();
	COM_METHOD(int) isUniversalServer ();
	COM_METHOD(int) sessionID ();

// IErrorRegister
	virtual void registerError (const char *errorMessage);

private:
	static void internalTimeOutCallback (int status);

	void applyIsolationLevel();
	void applyWaitOnLocks ();
	void internalConnect (_cstr_t& server, char *userName, char *password);
	bool isConnected ();
	void makeCurrent ();
	void makeDormant ();
	void refreshFeatures ();
	void saveConnectionArea ();
	void setQualifiedName (char *value);

	CInformixDriver *_driver;
	CInformixTypesMapping _typesMapping;
	bool _hasActivity;
	bool _isAnsiDatabase;
	bool _isInTransaction;
	sqlca_s _savedConnectionArea;
	TimeOutCallback _timeOutCallback;
	void *_timeOutCustomData;
	int _isUniversalServer;
	int _sessionID;
	typedef std::list<IDatabaseChangeObserver *> DatabaseChangeObservers;
	DatabaseChangeObservers _databaseChangeObservers;
	_cstr_t _clientLocale;
	_cstr_t _databaseLocale;
	bool _setIsolation;
	const char *_isolationLevel;
	ThreadMode _threadMode;

	                            // dbExpress connection options
	                            // ____________________________
	                            //
	bool _autoCommit;           // eConnAutoCommit
	int4 _blobSize;             // eConnBlobSize
	int _waitOnLocks;           // eConnWaitOnLocks
	int4 _commitRetain;         // eConnCommitRetain
	_cstr_t _handle;            // eConnNativeHandle
	_cstr_t _serverVersion;     // eConnServerVersion
	CCallback _callback;        // eConnCallBack
	                            // eConnCallBackInfo
	bool _supportsTransaction;  // eConnSupportsTransaction
	bool _isOnLine;             // eConnOnLine
	bool _trimChar;             // eConnTrimChar
	_cstr_t _qualifiedName;     // eConnQualifiedName
	char *_catalogName;         // eConnCatalogName
	char *_schemaName;          // eConnSchemaName
	char *_objectName;          // eConnObjectName
	_cstr_t _quotedObjectName;  // eConnQuotedObjectName


	class CConnectionActivity : public CInformixActivity
	{
	public:
		CConnectionActivity (CInformixConnection *connection);
		virtual ~CConnectionActivity ();
	private:
		CInformixConnection *_connection;
	};

	class CAutoDormantDecorator : public CInformixActivity
	{
	public:
		CAutoDormantDecorator (CInformixConnection *connection, CInformixActivityPtr& activity);
		virtual ~CAutoDormantDecorator ();
	private:
		CInformixConnection *_connection;
		CInformixActivityPtr _activity;
	};

	class CAutoCommitDecorator : public CInformixActivity
	{
	public:
		CAutoCommitDecorator (CInformixActivityPtr& activity);
		virtual ~CAutoCommitDecorator ();
	private:
		CInformixActivityPtr _activity;
	};

	class CConnectionAreaDecorator : public CInformixActivity
	{
	public:
		CConnectionAreaDecorator (CInformixConnection *connection, CInformixActivityPtr& activity);
		virtual ~CConnectionAreaDecorator ();
	private:
		CInformixConnection *_connection;
		CInformixActivityPtr _activity;
	};
};


class CInformixConnectionStringParser
{
public:
	static CInformixConnectionStringParser& getInstance ();
	void parse (char *connectionString, CInformixConnection *connectionContext);

private:
	static CInformixConnectionStringParser _instance;

	CInformixConnectionStringParser ();

	static void getNumericMapping (char *value, DataType& dataType);

	static void setAutoCommit (CInformixConnection *connectionContext, char *value);
	static void setBlobSize (CInformixConnection *connectionContext, char *value);
	static void setClientLocale (CInformixConnection *connectionContext, char *value);
	static void setCommitRetain (CInformixConnection *connectionContext, char *value);
	static void setDatabaseLocale (CInformixConnection *connectionContext, char *value);
	static void setDecimal (CInformixConnection *connectionContext, char *value);
	static void setInt8 (CInformixConnection *connectionContext, char *value);
	static void setLVarChar (CInformixConnection *connectionContext, char *value);
	static void setMoney (CInformixConnection *connectionContext, char *value);
	static void setThreadMode (CInformixConnection *connectionContext, char *value);
	static void setTrimChar (CInformixConnection *connectionContext, char *value);
	static void setWaitOnLocks (CInformixConnection *connectionContext, char *value);

	typedef void (*OptionFunc) (CInformixConnection *connectionContext, char *value);
	typedef HashTable<char *, OptionFunc, StringComparer> OptionHandlers;
	OptionHandlers _optionHandlers;
};

#endif // __IFXCONNECTION_HPP__
